/**
 * Copyright (C) 2016-2019 DSpot Sp. z o.o
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dspot.declex.override.holder;

import com.dspot.declex.action.Actions;
import com.dspot.declex.annotation.action.ActionFor;
import com.dspot.declex.annotation.action.StopOn;
import com.dspot.declex.api.action.base.BaseAbilityActionHolder;
import com.dspot.declex.api.action.process.ActionInfo;
import com.dspot.declex.api.action.process.ActionMethodParam;
import com.dspot.declex.api.action.processor.AbilityActionProcessor;
import com.dspot.declex.util.JavaDocUtils;
import com.dspot.declex.util.TypeUtils;
import com.dspot.declex.util.TypeUtils.ClassInformation;
import com.helger.jcodemodel.*;

import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.annotations.EBean;
import org.ohosannotations.annotations.Extra;
import org.ohosannotations.helper.CanonicalNameConstants;
import org.ohosannotations.holder.EAbilityHolder;
import org.ohosannotations.plugin.PluginClassHolder;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import static com.helger.jcodemodel.JExpr._new;

/**
 * 活动行动持有人
 *
 * @author ohos
 * @date 2021/07/27
 */
public class AbilityActionHolder extends PluginClassHolder<EAbilityHolder> {
    /**
     * 分钟片段支持sdk
     */
    private static final int MIN_SDK_WITH_FRAGMENT_SUPPORT = 11;
    /**
     * 意图的名字
     */
    private final static String INTENT_NAME = "intent";
    /**
     * 设置构建器
     */
    private final static String SET_BUILDER = "setBuilder";
    /**
     * 活动的行动
     */
    public JDefinedClass AbilityAction;
    /**
     * 施工现场
     */
    private JFieldVar builderField;

    /**
     * 活动行动持有人
     *
     * @param holder 持有人
     */
    public AbilityActionHolder(EAbilityHolder holder) {
        super(holder);
    }

    /**
     * 得到活动的行动
     *
     * @return {@link JDefinedClass}
     */
    public JDefinedClass getAbilityAction() {
        if (AbilityAction == null) {
            setAbilityAction();
        }
        return AbilityAction;
    }

    /**
     * 创建信息操作
     *
     * @param element 元素
     * @param env env
     */
    public static void createInformationForActionHolder(Element element, OhosAnnotationsEnvironment env) {

        final String clsName = element.asType().toString();
        final int index = clsName.lastIndexOf('.');
        final String pkg = clsName.substring(0, index);
        final String abilityName = clsName.substring(index + 1);
        final String actionName = pkg + "." + abilityName + "ActionHolder";

        ActionInfo actionInfo = new ActionInfo(actionName);
        actionInfo.processors.add(new AbilityActionProcessor());
        actionInfo.isGlobal = true;
        actionInfo.isTimeConsuming = false;
        actionInfo.superHolderClass = BaseAbilityActionHolder.class.getCanonicalName();

        //This will avoid generation for parent classes, not used in the project
        actionInfo.generated = false;

        actionInfo.setReferences(JavaDocUtils.referenceFromClassName(clsName));
        Actions.getInstance().addAction(abilityName, actionName, actionInfo);

        TypeElement superElement = env.getProcessingEnvironment()
            .getElementUtils()
            .getTypeElement(actionInfo.superHolderClass);
        Actions.getInstance().createInformationForMethods(superElement, actionInfo);

        addAbilityExtraInformation(actionInfo, element, env);
    }

    /**
     * 添加活动额外信息
     *
     * @param actionInfo 行动信息
     * @param element 元素
     * @param env env
     */
    private static void addAbilityExtraInformation(ActionInfo actionInfo, Element element, OhosAnnotationsEnvironment env) {
        List<Element> extraFields = new LinkedList<>();
        findExtraFields(element, extraFields, env.getProcessingEnvironment());

        for (Element fragmentArgField : extraFields) {
            addFieldInformationToActionHolder(actionInfo, fragmentArgField, env);
        }
    }

    /**
     * 添加字段信息持有者采取行动
     *
     * @param actionInfo 行动信息
     * @param element 元素
     * @param env env
     */
    private static void addFieldInformationToActionHolder(
        ActionInfo actionInfo, Element element,
        OhosAnnotationsEnvironment env) {

        final String clsName = element.getEnclosingElement().asType().toString();
        final int index = clsName.lastIndexOf('.');
        final String pkg = clsName.substring(0, index);
        final String abilityName = clsName.substring(index + 1);
        final String actionName = pkg + "." + abilityName + "ActionHolder";

        ClassInformation classInformation = TypeUtils.getClassInformation(element, env);
        final String className = classInformation.originalClassName;
        final String fieldName = element.getSimpleName().toString();

        AbstractJClass clazz = env.getJClass(className);
        if (classInformation.isList) {
            clazz = env.getClasses().LIST.narrow(clazz);
        }

        actionInfo.addMethod(
            fieldName,
            actionName,
            Arrays.asList(new ActionMethodParam(fieldName, clazz))
        );
    }

    /**
     * 找到额外的字段
     *
     * @param element 元素
     * @param fields 字段
     * @param env env
     */
    private static void findExtraFields(Element element, List<Element> fields, ProcessingEnvironment env) {

        List<? extends Element> elems = element.getEnclosedElements();
        for (Element elem : elems) {
            if (elem.getKind() == ElementKind.FIELD) {
                if (elem.getAnnotation(Extra.class) != null) {
                    fields.add(elem);
                }
            }
        }

        List<? extends TypeMirror> superTypes = env.getTypeUtils().directSupertypes(element.asType());
        for (TypeMirror type : superTypes) {
            TypeElement superElement = env.getElementUtils().getTypeElement(type.toString());
            if (superElement == null) continue;

            findExtraFields(superElement, fields, env);
        }

    }

    /**
     * 设置活动的行动
     */
    private void setAbilityAction() {
        try {
            final String clsName = holder().getAnnotatedElement().asType().toString();
            final int index = clsName.lastIndexOf('.');
            final String pkg = clsName.substring(0, index);
            final String abilityName = clsName.substring(index + 1);
            final String actionName = pkg + "." + abilityName + "ActionHolder";

            AbilityAction = getCodeModel()._class(actionName);
            AbilityAction._extends(BaseAbilityActionHolder.class);
            AbilityAction.annotate(EBean.class);

            JAnnotationUse actionFor = AbilityAction.annotate(ActionFor.class);
            actionFor.param("global", true);
            actionFor.param("value", abilityName);
            actionFor.param("timeConsuming", false);
            actionFor.param("processors", AbilityActionProcessor.class);

            ActionInfo actionInfo = Actions.getInstance().getActionInfos().get(AbilityAction.fullName());
            actionInfo.generated = true;
            AbilityAction.javadoc().add(actionInfo.references);

            setFields();
            setIntent();
            setBuilder();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置字段
     */
    private void setFields() {
        builderField = AbilityAction.field(JMod.PRIVATE, holder().getIntentBuilderClass(), "builder");
    }

    /**
     * 设置构建器
     */
    private void setBuilder() {
        AbstractJClass Context = getClasses().CONTEXT;
        AbstractJClass Fragment = getClasses().FRACTION;

        JMethod setBuilderMethod = AbilityAction.method(JMod.NONE, holder().getIntentBuilderClass(), SET_BUILDER);
        JVar param = setBuilderMethod.param(Context, "context");
        setBuilderMethod.body().assign(builderField, _new(holder().getIntentBuilderClass()).arg(param));
        setBuilderMethod.body()._return(builderField);

        if (hasFragmentInClasspath()) {
            setBuilderMethod = AbilityAction.method(JMod.NONE, holder().getIntentBuilderClass(), SET_BUILDER);
            param = setBuilderMethod.param(Fragment, "fragment");
            setBuilderMethod.body().assign(builderField, _new(holder().getIntentBuilderClass()).arg(param));
            setBuilderMethod.body()._return(builderField);
        }
    }

    /**
     * 在类路径片段
     *
     * @return boolean
     */
    protected boolean hasFragmentInClasspath() {
        return getProcessingEnvironment().getElementUtils().getTypeElement(CanonicalNameConstants.FRACTION) != null;
    }

    /**
     * 设置目的
     */
    private void setIntent() {
        JMethod intentMethod = AbilityAction.method(JMod.PUBLIC, holder().getIntentBuilderClass(), INTENT_NAME);
        intentMethod.annotate(Override.class);
        JAnnotationUse stopOn = intentMethod.annotate(StopOn.class);
        stopOn.param("value", "get");
        intentMethod.body()._return(builderField);
    }

}
